1 package org.apache.maven.surefire.junit4;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import java.util.ArrayList;
23 import java.util.Collection;
24 import java.util.Set;
25
26 import org.apache.maven.surefire.booter.Command;
27 import org.apache.maven.surefire.booter.MasterProcessListener;
28 import org.apache.maven.surefire.booter.MasterProcessReader;
29 import org.apache.maven.surefire.common.junit4.ClassMethod;
30 import org.apache.maven.surefire.common.junit4.JUnit4RunListener;
31 import org.apache.maven.surefire.common.junit4.JUnit4TestChecker;
32 import org.apache.maven.surefire.common.junit4.JUnitTestFailureListener;
33 import org.apache.maven.surefire.common.junit4.Notifier;
34 import org.apache.maven.surefire.providerapi.AbstractProvider;
35 import org.apache.maven.surefire.providerapi.ProviderParameters;
36 import org.apache.maven.surefire.report.ConsoleOutputReceiver;
37 import org.apache.maven.surefire.report.PojoStackTraceWriter;
38 import org.apache.maven.surefire.report.ReportEntry;
39 import org.apache.maven.surefire.report.ReporterFactory;
40 import org.apache.maven.surefire.report.RunListener;
41 import org.apache.maven.surefire.report.SimpleReportEntry;
42 import org.apache.maven.surefire.suite.RunResult;
43 import org.apache.maven.surefire.testset.TestListResolver;
44 import org.apache.maven.surefire.testset.TestRequest;
45 import org.apache.maven.surefire.testset.TestSetFailedException;
46 import org.apache.maven.surefire.util.RunOrderCalculator;
47 import org.apache.maven.surefire.util.ScanResult;
48 import org.apache.maven.surefire.util.TestsToRun;
49 import org.junit.runner.Description;
50 import org.junit.runner.Result;
51 import org.junit.runner.Runner;
52 import org.junit.runner.manipulation.Filter;
53 import org.junit.runner.notification.StoppedByUserException;
54
55 import static org.apache.maven.surefire.common.junit4.JUnit4ProviderUtil.createSuiteDescription;
56 import static org.apache.maven.surefire.common.junit4.JUnit4ProviderUtil.cutTestClassAndMethod;
57 import static org.apache.maven.surefire.common.junit4.JUnit4ProviderUtil.generateFailingTests;
58 import static org.apache.maven.surefire.common.junit4.JUnit4Reflector.createDescription;
59 import static org.apache.maven.surefire.common.junit4.JUnit4Reflector.createIgnored;
60 import static org.apache.maven.surefire.common.junit4.JUnit4RunListener.rethrowAnyTestMechanismFailures;
61 import static org.apache.maven.surefire.common.junit4.JUnit4RunListenerFactory.createCustomListeners;
62 import static org.apache.maven.surefire.report.ConsoleOutputCapture.startCapture;
63 import static org.apache.maven.surefire.report.SimpleReportEntry.withException;
64 import static org.apache.maven.surefire.testset.TestListResolver.toClassFileName;
65 import static org.apache.maven.surefire.util.TestsToRun.fromClass;
66 import static org.junit.runner.Request.aClass;
67 import static org.junit.runner.Request.method;
68 import static java.lang.reflect.Modifier.isAbstract;
69 import static java.lang.reflect.Modifier.isInterface;
70 import static java.util.Collections.unmodifiableCollection;
71
72
73
74
75 public class JUnit4Provider
76 extends AbstractProvider
77 {
78 private static final String UNDETERMINED_TESTS_DESCRIPTION = "cannot determine test in forked JVM with surefire";
79
80 private final ClassLoader testClassLoader;
81
82 private final Collection<org.junit.runner.notification.RunListener> customRunListeners;
83
84 private final JUnit4TestChecker jUnit4TestChecker;
85
86 private final TestListResolver testResolver;
87
88 private final ProviderParameters providerParameters;
89
90 private final RunOrderCalculator runOrderCalculator;
91
92 private final ScanResult scanResult;
93
94 private final int rerunFailingTestsCount;
95
96 private final MasterProcessReader commandsReader;
97
98 private TestsToRun testsToRun;
99
100 public JUnit4Provider( ProviderParameters booterParameters )
101 {
102
103 commandsReader = booterParameters.isInsideFork()
104 ? MasterProcessReader.getReader().setShutdown( booterParameters.getShutdown() )
105 : null;
106 providerParameters = booterParameters;
107 testClassLoader = booterParameters.getTestClassLoader();
108 scanResult = booterParameters.getScanResult();
109 runOrderCalculator = booterParameters.getRunOrderCalculator();
110 String listeners = booterParameters.getProviderProperties().get( "listener" );
111 customRunListeners = unmodifiableCollection( createCustomListeners( listeners ) );
112 jUnit4TestChecker = new JUnit4TestChecker( testClassLoader );
113 TestRequest testRequest = booterParameters.getTestRequest();
114 testResolver = testRequest.getTestListResolver();
115 rerunFailingTestsCount = testRequest.getRerunFailingTestsCount();
116 }
117
118 public RunResult invoke( Object forkTestSet )
119 throws TestSetFailedException
120 {
121 if ( isRerunFailingTests() && isFailFast() )
122 {
123 throw new TestSetFailedException( "don't enable parameters rerunFailingTestsCount, skipAfterFailureCount" );
124 }
125
126 if ( testsToRun == null )
127 {
128 if ( forkTestSet instanceof TestsToRun )
129 {
130 testsToRun = (TestsToRun) forkTestSet;
131 }
132 else if ( forkTestSet instanceof Class )
133 {
134 testsToRun = fromClass( (Class<?>) forkTestSet );
135 }
136 else
137 {
138 testsToRun = scanClassPath();
139 }
140 }
141
142 upgradeCheck();
143
144 ReporterFactory reporterFactory = providerParameters.getReporterFactory();
145
146 RunListener reporter = reporterFactory.createReporter();
147
148 startCapture( (ConsoleOutputReceiver) reporter );
149
150 Notifier notifier = new Notifier( new JUnit4RunListener( reporter ), getSkipAfterFailureCount() );
151 if ( isFailFast() )
152 {
153 notifier.addListener( new JUnit4FailFastListener( notifier ) );
154 }
155 Result result = new Result();
156 notifier.addListeners( customRunListeners )
157 .addListener( result.createListener() );
158
159 if ( isFailFast() && commandsReader != null )
160 {
161 registerPleaseStopJunitListener( notifier );
162 }
163
164 try
165 {
166 notifier.fireTestRunStarted( testsToRun.allowEagerReading()
167 ? createTestsDescription()
168 : createDescription( UNDETERMINED_TESTS_DESCRIPTION ) );
169
170 if ( commandsReader != null )
171 {
172 commandsReader.addShutdownListener( new MasterProcessListener()
173 {
174 public void update( Command command )
175 {
176 testsToRun.markTestSetFinished();
177 }
178 } );
179 commandsReader.awaitStarted();
180 }
181
182 for ( Class<?> aTestsToRun : testsToRun )
183 {
184 executeTestSet( aTestsToRun, reporter, notifier );
185 }
186 }
187 finally
188 {
189 notifier.fireTestRunFinished( result );
190 notifier.removeListeners();
191 closeCommandsReader();
192 }
193
194 rethrowAnyTestMechanismFailures( result );
195 return reporterFactory.close();
196 }
197
198 private boolean isRerunFailingTests()
199 {
200 return rerunFailingTestsCount > 0;
201 }
202
203 private boolean isFailFast()
204 {
205 return providerParameters.getSkipAfterFailureCount() > 0;
206 }
207
208 private int getSkipAfterFailureCount()
209 {
210 return isFailFast() && !isRerunFailingTests() ? providerParameters.getSkipAfterFailureCount() : 0;
211 }
212
213 private void closeCommandsReader()
214 {
215 if ( commandsReader != null )
216 {
217 commandsReader.stop();
218 }
219 }
220
221 private MasterProcessListener registerPleaseStopJunitListener( final Notifier notifier )
222 {
223 MasterProcessListener listener = new MasterProcessListener()
224 {
225 public void update( Command command )
226 {
227 notifier.pleaseStop();
228 }
229 };
230 commandsReader.addSkipNextListener( listener );
231 return listener;
232 }
233
234 private void executeTestSet( Class<?> clazz, RunListener reporter, Notifier notifier )
235 {
236 final ReportEntry report = new SimpleReportEntry( getClass().getName(), clazz.getName() );
237 reporter.testSetStarting( report );
238 try
239 {
240 executeWithRerun( clazz, notifier );
241 }
242 catch ( Throwable e )
243 {
244 if ( isFailFast() && e instanceof StoppedByUserException )
245 {
246 String reason = e.getClass().getName();
247 Description skippedTest = createDescription( clazz.getName(), createIgnored( reason ) );
248 notifier.fireTestIgnored( skippedTest );
249 }
250 else
251 {
252 String reportName = report.getName();
253 String reportSourceName = report.getSourceName();
254 PojoStackTraceWriter stackWriter = new PojoStackTraceWriter( reportSourceName, reportName, e );
255 reporter.testError( withException( reportSourceName, reportName, stackWriter ) );
256 }
257 }
258 finally
259 {
260 reporter.testSetCompleted( report );
261 }
262 }
263
264 private void executeWithRerun( Class<?> clazz, Notifier notifier ) throws TestSetFailedException
265 {
266 JUnitTestFailureListener failureListener = new JUnitTestFailureListener();
267 notifier.addListener( failureListener );
268 boolean hasMethodFilter = testResolver != null && testResolver.hasMethodPatterns();
269 execute( clazz, notifier, hasMethodFilter ? new TestResolverFilter() : new NullFilter() );
270
271
272 if ( isRerunFailingTests() )
273 {
274 for ( int i = 0; i < rerunFailingTestsCount && !failureListener.getAllFailures().isEmpty(); i++ )
275 {
276 Set<ClassMethod> failedTests = generateFailingTests( failureListener.getAllFailures() );
277 failureListener.reset();
278 if ( !failedTests.isEmpty() )
279 {
280 executeFailedMethod( notifier, failedTests );
281 }
282 }
283 }
284 }
285
286 public Iterable<Class<?>> getSuites()
287 {
288 testsToRun = scanClassPath();
289 return testsToRun;
290 }
291
292 private TestsToRun scanClassPath()
293 {
294 final TestsToRun scannedClasses = scanResult.applyFilter( jUnit4TestChecker, testClassLoader );
295 return runOrderCalculator.orderTestClasses( scannedClasses );
296 }
297
298 private void upgradeCheck()
299 throws TestSetFailedException
300 {
301 if ( isJUnit4UpgradeCheck() )
302 {
303 Collection<Class<?>> classesSkippedByValidation =
304 scanResult.getClassesSkippedByValidation( jUnit4TestChecker, testClassLoader );
305 if ( !classesSkippedByValidation.isEmpty() )
306 {
307 StringBuilder reason = new StringBuilder();
308 reason.append( "Updated check failed\n" );
309 reason.append( "There are tests that would be run with junit4 / surefire 2.6 but not with [2.7,):\n" );
310 for ( Class testClass : classesSkippedByValidation )
311 {
312 reason.append( " " );
313 reason.append( testClass.getName() );
314 reason.append( "\n" );
315 }
316 throw new TestSetFailedException( reason.toString() );
317 }
318 }
319 }
320
321 private Description createTestsDescription()
322 {
323 Collection<Class<?>> classes = new ArrayList<Class<?>>();
324 for ( Class<?> clazz : testsToRun )
325 {
326 classes.add( clazz );
327 }
328 return createSuiteDescription( classes );
329 }
330
331 private static boolean isJUnit4UpgradeCheck()
332 {
333 return System.getProperty( "surefire.junit4.upgradecheck" ) != null;
334 }
335
336 private static void execute( Class<?> testClass, Notifier notifier, Filter filter )
337 {
338 final int classModifiers = testClass.getModifiers();
339 if ( !isAbstract( classModifiers ) && !isInterface( classModifiers ) )
340 {
341 Runner runner = aClass( testClass ).filterWith( filter ).getRunner();
342 if ( countTestsInRunner( runner.getDescription() ) != 0 )
343 {
344 runner.run( notifier );
345 }
346 }
347 }
348
349 private void executeFailedMethod( Notifier notifier, Set<ClassMethod> failedMethods )
350 throws TestSetFailedException
351 {
352 for ( ClassMethod failedMethod : failedMethods )
353 {
354 try
355 {
356 Class<?> methodClass = Class.forName( failedMethod.getClazz(), true, testClassLoader );
357 String methodName = failedMethod.getMethod();
358 method( methodClass, methodName ).getRunner().run( notifier );
359 }
360 catch ( ClassNotFoundException e )
361 {
362 throw new TestSetFailedException( "Unable to create test class '" + failedMethod.getClazz() + "'", e );
363 }
364 }
365 }
366
367
368
369
370
371
372
373
374 private static int countTestsInRunner( Description description )
375 {
376 if ( description.isSuite() )
377 {
378 int count = 0;
379 for ( Description child : description.getChildren() )
380 {
381 if ( !hasFilteredOutAllChildren( child ) )
382 {
383 count += countTestsInRunner( child );
384 }
385 }
386 return count;
387 }
388 else if ( description.isTest() )
389 {
390 return hasFilteredOutAllChildren( description ) ? 0 : 1;
391 }
392 else
393 {
394 return 0;
395 }
396 }
397
398 private static boolean hasFilteredOutAllChildren( Description description )
399 {
400 String name = description.getDisplayName();
401
402 if ( name == null )
403 {
404 return true;
405 }
406 else
407 {
408 name = name.trim();
409 return name.startsWith( "initializationError0(org.junit.runner.manipulation.Filter)" )
410 || name.startsWith( "initializationError(org.junit.runner.manipulation.Filter)" );
411 }
412 }
413
414 private class TestResolverFilter
415 extends Filter
416 {
417 private final TestListResolver methodFilter = JUnit4Provider.this.testResolver.createMethodFilters();
418
419 @Override
420 public boolean shouldRun( Description description )
421 {
422
423 final ClassMethod cm = cutTestClassAndMethod( description );
424 final boolean isSuite = description.isSuite();
425 final boolean isValidTest = description.isTest() && cm.isValid();
426 final String clazz = cm.getClazz();
427 final String method = cm.getMethod();
428 return isSuite || isValidTest && methodFilter.shouldRun( toClassFileName( clazz ), method );
429 }
430
431 @Override
432 public String describe()
433 {
434 return methodFilter.toString();
435 }
436 }
437
438 private final class NullFilter
439 extends TestResolverFilter
440 {
441
442 @Override
443 public boolean shouldRun( Description description )
444 {
445 return true;
446 }
447
448 @Override
449 public String describe()
450 {
451 return "";
452 }
453 }
454 }